import pickle
import plotly.express as px
import dash
from dash import dcc, html, Input, Output
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import plotly
import plotly.colors
import re
import plotly.figure_factory as ff
import warnings
# Suppress FutureWarning messages
warnings.simplefilter(action='ignore', category=FutureWarning)
def get_continuous_color(colorscale, intermed):
"""
Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
color for any value in that range.
Plotly doesn't make the colorscales directly accessible in a common format.
Some are ready to use:
colorscale = plotly.colors.PLOTLY_SCALES["Greens"]
Others are just swatches that need to be constructed into a colorscale:
viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)
:param colorscale: A plotly continuous colorscale defined with RGB string colors.
:param intermed: value in the range [0, 1]
:return: color in rgb string format
:rtype: str
"""
if len(colorscale) < 1:
raise ValueError("colorscale must have at least one color")
if intermed <= 0 or len(colorscale) == 1:
return colorscale[0][1]
if intermed >= 1:
return colorscale[-1][1]
for cutoff, color in colorscale:
if intermed > cutoff:
low_cutoff, low_color = cutoff, color
else:
high_cutoff, high_color = cutoff, color
break
# noinspection PyUnboundLocalVariable
return plotly.colors.find_intermediate_color(
lowcolor=low_color, highcolor=high_color,
intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
colortype="rgb")
def convert_rgb_to_int(rgb_string):
# Use a regular expression to find all floating point numbers in the string
numbers = re.findall(r'\d+\.\d+', rgb_string)
# Convert each number to an integer
int_numbers = [str(int(float(num))) for num in numbers]
# Replace the original floating point numbers with the converted integers in the string
for num, int_num in zip(numbers, int_numbers):
rgb_string = rgb_string.replace(num, int_num)
return rgb_string
def format_colorscale(colorscale):
formatted_colorscale = []
for value, color in colorscale:
# Format the float to two decimal places
formatted_value = float(f"{value:.2f}")
# Use regular expressions to find the numbers in the color string and convert them to integers
formatted_color = re.sub(r'\d+\.\d+', lambda x: str(int(float(x.group()))), color)
# Append the formatted value and color to the new colorscale list
formatted_colorscale.append([formatted_value, formatted_color])
return formatted_colorscale
with open('Data/key_distribution', 'rb') as f:
data = pickle.load(f)
data = data * 20000
cks = pd.Series([list(data[let]) for let in data.columns], index = data.columns, dtype = object)
data = data.transpose()
display(data.head())
# Calculate the frequency distribution for each key across the left, center, and right sections
extremes = data.iloc[:, [0, 1, 2, 7, 8, 9, 20, 21, 22, 27, 28, 29]].sum(axis=1)
home_row = data.iloc[:, [10, 11, 12, 13, 16, 17, 18, 19]].sum(axis=1)
center = data.iloc[:, [3, 4, 5, 6, 14, 15, 23, 24, 25, 26]].sum(axis=1)
columns = ['extremes', 'home_row', 'center']
# Combine the results into a single DataFrame
frequency_distribution_sections = pd.DataFrame({
columns[0]: eval(columns[0]),
columns[1]: eval(columns[1]),
columns[2]: eval(columns[2]),
})
frequency_distribution_sections.reset_index(inplace=True)
frequency_distribution_sections = frequency_distribution_sections.melt(id_vars=["index"], var_name="Section", value_name="Frequency")
pvt = frequency_distribution_sections.pivot("index", "Section", "Frequency")
pvt = pvt[columns]
pvt = pvt.iloc[4:].append(pvt.iloc[:4])
pvt = (pvt/1026).round(3) * 100
n = np.array(pvt)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| q | 130.0 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 104.0 | ... | 388.0 | 1.0 | 0.0 | 0.0 | 6.0 | 0.0 | 0.0 | 0.0 | 2.0 | 393.0 |
| w | 0.0 | 37.0 | 31.0 | 0.0 | 331.0 | 0.0 | 0.0 | 37.0 | 44.0 | 0.0 | ... | 0.0 | 1.0 | 215.0 | 33.0 | 0.0 | 50.0 | 41.0 | 206.0 | 0.0 | 0.0 |
| e | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| r | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| t | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
5 rows × 30 columns
viridis_colors, _ = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Reds)
colorscale = plotly.colors.make_colorscale(viridis_colors)
get_continuous_color(colorscale, 1)
colors = []
colors.append([0.00, 'rgb(255, 255, 255)'])
colors.append([0.22, 'rgb(255, 255, 255)'])
colors.append([0.46, 'rgb(254, 254, 254)'])
viridis_colors, _ = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Purples)
colorscale = plotly.colors.make_colorscale(viridis_colors)
# 16
for i in range(1, 13):
val = (44 + i * 2 - 45)
cl = get_continuous_color(colorscale, val/65)
colors.append([(val + 45) * 0.01,convert_rgb_to_int(cl)])
colors.append([1, (get_continuous_color(colorscale, 55/80))])
colors = format_colorscale(colors)
z = pvt.values
# Create text matrix of the same dimension as z
text = [[str("") for value in row] for row in z]
fig = ff.create_annotated_heatmap(n[::-1],
x=list(pvt.columns), y= list(reversed(list(pvt.index))),
colorscale=colors, showscale=True, font_colors = ['black'], zmin = 0, zmax = 100)
# Update layout to ensure x-axis labels are at the bottom
fig.update_layout(
title="Frequency Distribution of Keys Across Keyboard Sections",
yaxis_title="Key",
margin=dict(l=0, r=0, t=60, b=10) # Adjust margins to remove whitespace
)
fig.update_traces(
hoverinfo='text',
hovertemplate='%{text}<extra></extra>',
text=text
)
text = [[f"{val:.2f}" for val in row] for row in n[::-1]]
# Update traces to use the formatted text annotations
for i in range(len(fig.layout.annotations)):
fig.layout.annotations[i].text = text[i // len(n[0])][i % len(n[0])]
fig.show()
# Calculate the frequency distribution for each key across the left, center, and right sections
top = data.iloc[:, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]].sum(axis=1)
bottom = data.iloc[:, [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]].sum(axis=1)
columns = ['top', 'bottom']
# Combine the results into a single DataFrame
frequency_distribution_sections = pd.DataFrame({
columns[0]: eval(columns[0]),
columns[1]: eval(columns[1]),
})
frequency_distribution_sections.reset_index(inplace=True)
frequency_distribution_sections = frequency_distribution_sections.melt(id_vars=["index"], var_name="Section", value_name="Frequency")
pvt = frequency_distribution_sections.pivot("index", "Section", "Frequency")
pvt = pvt[columns]
pvt = pvt.iloc[4:].append(pvt.iloc[:4])
pvt = (pvt/1026).round(3) * 100
n = np.array(pvt)
colors = []
colors.append([0.00, 'rgb(255, 255, 255)'])
colors.append([0.22, 'rgb(255, 255, 255)'])
colors.append([0.60, 'rgb(254, 254, 254)'])
viridis_colors, _ = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Purples)
colorscale = plotly.colors.make_colorscale(viridis_colors)
# 16
for i in range(1, 10):
val = (60 + i * 2 - 60)
cl = get_continuous_color(colorscale, val/40)
colors.append([(val + 60) * 0.01,convert_rgb_to_int(cl)])
colors.append([1, (get_continuous_color(colorscale, 55/80))])
colors = format_colorscale(colors)
z = pvt.values
# Create text matrix of the same dimension as z
text = [[str("") for value in row] for row in z]
fig = ff.create_annotated_heatmap(n[::-1],
x=list(pvt.columns), y= list(reversed(list(pvt.index))),
colorscale=colors, showscale=True, font_colors = ['black'], zmin = 0, zmax = 100)
# Update layout to ensure x-axis labels are at the bottom
fig.update_layout(
title="Frequency Distribution of Keys For Top and Bottom Rows",
yaxis_title="Key",
margin=dict(l=0, r=0, t=60, b=10) # Adjust margins to remove whitespace
)
fig.update_traces(
hoverinfo='text',
hovertemplate='%{text}<extra></extra>',
text=text
)
text = [[f"{val:.2f}" for val in row] for row in n[::-1]]
# Update traces to use the formatted text annotations
for i in range(len(fig.layout.annotations)):
fig.layout.annotations[i].text = text[i // len(n[0])][i % len(n[0])]
fig.show()
file = 'Data/war_and_peace_text.txt'
lines = []
# Open the file and read line by line
with open(file, 'r') as file:
for line in file:
# Strip the newline character at the end of each line
s = line.split(':')
let = s[0].strip()
val = int(s[1].strip())
lines.append([let, val])
# Convert the list of lines into a DataFrame
#lets = pd.DataFrame(lines, columns=['Line'])
#lets
df = pd.DataFrame(lines)
df = df.set_index(0)
df.columns = ['percs']
df['percs'] = df['percs'] / df['percs'].sum() * 100
import numpy as np
import plotly.graph_objs as go
from plotly.subplots import make_subplots
# Define the towers function
def towers(a, e, pos_x, pos_y, color, text):
# create points
x, y, z = np.meshgrid(
np.linspace(pos_x - a / 2, pos_x + a / 2, 2),
np.linspace(pos_y - a / 2, pos_y + a / 2, 2),
np.linspace(0, e, 2)
)
x = x.flatten()
y = y.flatten()
z = z.flatten()
return go.Mesh3d(x=x, y=y, z=z, alphahull=1, flatshading=True, color=color, hovertext=text, hoverinfo='text')
# Modify the get_keyboard function to return the traces
def get_keyboard(keys, df):
best_list = [list(keys[i:i+10]) for i in range(0, len(keys), 10)]
l = []
for b in best_list:
for i in b:
l.append(i)
vals = []
for i in l:
try:
vals.append(df.loc[i]['percs'])
except:
vals.append(0.01) # Default value if key is not found
# dimensions of global grid
x_dim = 10
y_dim = 3
x, y = np.meshgrid(
np.arange(x_dim),
np.arange(y_dim)
)
x = x[:, ::-1]
xx = x.flatten()
yy = y.flatten()
# Normalize z values to get shades of gray
min_val = min(vals)
max_val = max(vals)
normalized_vals = [(v - min_val) / (max_val - min_val) * 255 for v in vals]
# Grayscale colors based on z values
colors = [f'rgba({int(v)}, {int(v)}, {int(v)}, 1)' for v in normalized_vals]
traces = []
for i, (x, y, z) in enumerate(zip(xx, yy, vals)):
traces.append(towers(1, z, x, y, colors[i], l[i]))
return traces
# Example usage
keys1 = "qwertyuiopasdfghjkl;zxcvbnm,./"
keys2 = "/.xsglv,mjetcpnohariq;yfbkduwz"
# Assuming df is a DataFrame with the necessary data
# Create traces for two different key configurations
traces1 = get_keyboard(keys1, df)
traces2 = get_keyboard(keys2, df)
# Create subplots
fig = make_subplots(rows=1, cols=2, specs=[[{'is_3d': True}, {'is_3d': True}]],
subplot_titles=("QWERTY", "Optimal Keyboard")
)
for trace in traces2:
fig.add_trace(trace, row=1, col=2)
for trace in traces1:
fig.add_trace(trace, row=1, col=1)
# Define the camera view for the first subplot
camera1 = dict(
up=dict(x=0, y=0, z=1),
center=dict(x=0, y=0, z=0),
eye=dict(x=1.2, y=2.2, z=.9)
)
# Define the camera view for the second subplot with slight adjustments
camera2 = dict(
up=dict(x=0, y=0, z=1),
center=dict(x=0, y=0, z=0),
eye=dict(x=1.6, y=1.6, z=1.6)
)
fig.update_layout(
scene=dict(
domain = dict(x = [0, 0.5]),
bgcolor='rgba(255, 255, 255, 1)', # White background for the first subplot
xaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
yaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
zaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
camera=camera1 # Apply the camera configuration for the first subplot
),
scene2=dict(
domain = dict(x = [.5, 1]),
bgcolor='rgba(255, 255, 255, 1)', # Light grey background for the second subplot
xaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
yaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
zaxis=dict(showbackground=True, showgrid=True, showticklabels=False, title=''),
camera=camera1 # Apply the camera configuration for the second subplot
),
margin=dict(l=0, r=0, t=20, b=0)
)
for i in range(len(fig['layout']['annotations'])):
fig['layout']['annotations'][i]['font'] = dict(size=25, color='black')
fig['layout']['annotations'][i]['y'] = fig['layout']['annotations'][i]['y'] - 0.05 # Adjust y to reduce space
fig
def clicks_to_colors(clicks):
"""
Convert click values to a list of colors using the OrRd color scale.
Values between 1 and 25 are mapped to the color gradient. Values of 0 are mapped to white.
"""
# Mint
# Dense
# Burg
colorscale = px.colors.sequential.Mint
max_click = 440
min_click = 50
colors_list = []
for click in clicks:
if click > max_click:
colors_list.append(colorscale[-1])
elif click == 0:
colors_list.append("rgba(255, 255, 255, 1)") # White for 0 clicks
else:
# Normalize the click value between min_click and max_click
normalized_click = (click+0.8) / (max_click+0.8)
# Map normalized click to a color in the colorscale
color_idx = int(normalized_click * (len(colorscale) - 1))
colors_list.append(colorscale[color_idx])
return colors_list
# Function to get the coordinates of a letter on a QWERTY keyboard
def get_qwerty_coordinates(letter):
for y, row in enumerate(qwerty_layout):
if letter in row:
x = row.index(letter)
if y == 0:
return x, y
elif y == 1:
return x + 0.25, y
else:
return x + 0.75, y
return None
# Function to create a box (rectangle) trace with optional annotation
def create_box(x0, y0, x1, y1, color, letter=None):
return go.Scatter(
x=[x0, x1, x1, x0, x0], # Coordinates for the rectangle
y=[y0, y0, y1, y1, y0], # Coordinates for the rectangle
fill='toself', # Fill the box
fillcolor=color, # Color to fill
line=dict(color='black'), # Border color
mode='lines+text' if letter else 'lines', # Mode for the plot
text=[letter] if letter else None, # The letter annotation
textposition='bottom left',
showlegend=False,
hoverinfo='none', # Hide trace info
hovertemplate=f'%<extra></extra>', # Custom hovertemplate
)
# Function to generate the keyboard layout with offsets
def generate_keyboard_boxes(colors, selected_letter=None):
x = np.arange(10) # X positions for the keys
y = np.array([2, 1, 0]) # Y positions for the rows
offsets = [0, 0.25, 0.75] # Offsets for the rows
boxes = []
color_idx = 0 # Color index
for i in range(len(y)): # For each row
for j in range(len(x)): # For each key in the row
x0 = x[j] + offsets[i] # Calculate x0 with offset
y0 = y[i] # y0 position
x1 = x0 + 1 # x1 position
y1 = y0 + 1 # y1 position
letter = None
if selected_letter and cks[f'{selected_letter}'] == clicks_to_colors(cks[f'{selected_letter}'])[i * len(x) + j]:
letter = selected_letter.upper()
boxes.append(create_box(x0, y0, x1, y1, colors[color_idx % len(colors)], letter)) # Create and append the box with optional letter
color_idx += 1 # Increment color index
return boxes
# Define the QWERTY keyboard layout
qwerty_layout = [
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/']
]
# Get default colors for the initial display
default_colors = clicks_to_colors(cks['q'])
letter_buttons = [{
'label': f'{a}',
'method': 'update',
'args': [
{'fillcolor': clicks_to_colors(cks[f'{a}'])},
{'title': f'Key Distribution For',
'annotations': [
dict(
x=(get_qwerty_coordinates(f'{a}')[0] + 0.5), # Center x position of the box
y=(2 - get_qwerty_coordinates(f'{a}')[1] + 0.5), # Center y position of the box, assuming y positions are reversed
text=f'{a}'.upper(), # The letter in uppercase
showarrow=False,
font=dict(size=23, color='black')
)
]
}
]
} for a in data.index]
# Initial boxes with default color
boxes = generate_keyboard_boxes(default_colors, 'q')
boxes[0] = go.Scatter({
'fill': 'toself',
'fillcolor': 'rgb(137, 192, 182)',
'line': {'color': 'black'},
'mode': 'lines+text',
'showlegend': False,
'x': [0, 1, 1, 0, 0],
'y': [2, 2, 3, 3, 2]
})
# Create the figure
fig = go.Figure()
# Add the initial boxes to the figure
for box in boxes:
fig.add_trace(box)
# Define the manual position for the text
text_x = 0.5 # Middle of the box in the x-axis
text_y = 2.5 # Middle of the box in the y-axis
# Add annotation for the text
fig.add_annotation(
x=text_x,
y=text_y,
text="Q",
showarrow=False,
font=dict(size=28),
xanchor='center',
yanchor='middle'
)
# Dropdown menu for color selection
updatemenus = [
{
'buttons': letter_buttons, # Attach the letter buttons
'direction': 'down', # Dropdown direction
'showactive': True, # Show the active button
'x': 0.365, # x position of the dropdown
'xanchor': 'center', # x anchor position
'y': 1.025, # y position of the dropdown
'yanchor': 'top', # y anchor position
'font': dict(size=20), # Set the font size for the dropdown
}
]
# Update the layout to include the dropdown menu
fig.update_layout(
title=dict(
text='Key Distribution For',
font=dict(size=33), # Set the font size for the title
y=0.93, # Adjust the vertical position of the title
x=.15, # Center the title horizontally
xanchor='center',
yanchor='top'
),
updatemenus=updatemenus,
xaxis=dict(showgrid=False, zeroline=False, visible=False),
yaxis=dict(showgrid=False, zeroline=False, visible=False),
width=1000,
height=400,
plot_bgcolor='white',
)
# Create a dummy heatmap for the colorscale legend
fig.add_trace(go.Heatmap(
z=[[400, 2200]], # Dummy data for the heatmap
colorscale='Mint', # Choose a colorscale
showscale=True, # Show the scale
colorbar=dict(
orientation='v',
x=1.05,
y=0.93,
thickness=15,
len=0.8,
xanchor='center',
yanchor='top',
tickvals=[400, 1000, 1600, 2200], # Custom tick values
ticktext=['4%', '10%', '16%', '24%']
),
opacity = 0,
hoverinfo='none'
))
# Update layout to ensure x-axis labels are at the bottom
fig.update_layout(
margin=dict(l=0, r=0, t=60, b=0) # Adjust margins to remove whitespace
)
# Show the plot
fig.show()
def calculate_entropy(distribution):
# Normalize the distribution to get probabilities
probabilities = distribution / np.sum(distribution)
# Calculate entropy
entropy = -np.sum(probabilities * np.log(probabilities + 1e-9)) # adding a small value to avoid log(0)
return entropy
# Calculate entropy for each letter
entropies = {letter: calculate_entropy(np.array(distribution)) for letter, distribution in cks.items()}
# Normalize the entropy values to a range of [0, 1]
max_entropy = np.log(30) # The maximum possible entropy for 30 spots (uniform distribution)
normalized_entropies = {letter: entropy / max_entropy for letter, entropy in entropies.items()}
# Output the results
k = dict(sorted(normalized_entropies.items(),key=lambda x:x[1],reverse = True))
k
{'w': 0.5552318618446004,
',': 0.5352788755955837,
'b': 0.53373853790098,
'j': 0.5213352305879383,
'v': 0.5056730147802252,
'a': 0.5047236757513366,
'y': 0.5025116716131943,
'x': 0.4852808896906422,
's': 0.48474979069741186,
'.': 0.4667051991966148,
';': 0.4341400190372003,
'o': 0.43249029300144104,
'f': 0.4295995673178524,
'i': 0.4267866063560094,
'g': 0.40851724057716315,
'/': 0.4000346479544242,
'z': 0.3968369990218024,
't': 0.39241642404082283,
'q': 0.37974866881154307,
'l': 0.35375172180049774,
'k': 0.3532568500324857,
'e': 0.34887196668196946,
'c': 0.34325037032251576,
'p': 0.3289777996656306,
'h': 0.3287734385898439,
'd': 0.3284891025429945,
'r': 0.31808728566153666,
'm': 0.3091655062752482,
'u': 0.20379281208392103,
'n': 0.15276060478762374}
# Columns come first, Index come second
data = pd.read_csv('Data/times_pressed.csv', index_col=0)
data = data.iloc[:, 1:]
data = data.transpose()
data.head()
| q | w | e | r | t | y | u | i | o | ... | z | x | c | v | b | n | m | , | . | / | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| q | 195056067 | 308002 | 152266 | 124260298 | 3610747 | 785022 | 213865 | 1444774 | 38816988 | 2199836 | ... | 221018 | 822865 | 11418816 | 111577 | 1072450 | 9125100 | 411526 | 72254 | 312015 | 500564 |
| w | 4638390461 | 586402 | 34394960 | 438822834 | 46903733 | 226215572 | 23514033 | 2489845 | 4549773 | 1125725016 | ... | 834072 | 766541 | 1841061 | 558799 | 2809747 | 18837412 | 5533258 | 821324 | 5122126 | 12157861 |
| e | 2142108028 | 483314 | 1200604260 | 1269199466 | 5627498048 | 3603683062 | 377423552 | 424930068 | 1134528986 | 129457623 | ... | 166245727 | 60045779 | 1858115400 | 2693870666 | 1680561415 | 2255840336 | 2468704447 | 680736 | 9213826 | 4635570 |
| r | 2441597601 | 712697 | 98351248 | 5990121323 | 331049184 | 1227590455 | 28950845 | 1928762782 | 942738458 | 4110770057 | ... | 924808 | 782400 | 509886799 | 5404881 | 375442750 | 25686685 | 26237299 | 490788 | 4840019 | 4562133 |
| t | 12164315875 | 712811 | 18572520 | 1428979217 | 1152100849 | 565565952 | 64983155 | 1346985955 | 3480571544 | 1264281278 | ... | 985324 | 139069618 | 1228433831 | 1901068 | 32002988 | 2989255817 | 8619859 | 1312797 | 10339044 | 6434917 |
5 rows × 31 columns
sums = pd.DataFrame(index = data.index, columns = ['freq'])
sums['freq'] = [data.loc[d].sum() for d in data.index]
sums = sums.sort_values(by = 'freq')/sums.sum() * 100
colemak = ['a', 'r', 's', 't', 'd', 'h', 'n', 'e', 'i', 'o']
dvorak = ['a', 'o', 'e', 'u', 'i', 'd', 'h', 't', 'n', 's']
qwerty = ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';']
alphabet = ['k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't']
ours = ['i', 'e', 't', 'r', 'h', 'd', 'n', 'o', 's', 'a']
vals = [colemak, dvorak, qwerty, alphabet, ours]
for v in vals:
print(sums.loc[v].sum().freq)
70.80162991597203 67.48919934452486 32.01422440496837 45.53830235381878 70.80162991597203
def clicks_to_colors3(clicks, min, max):
"""
Convert click values to a list of colors using the OrRd color scale.
Values between 1 and 25 are mapped to the color gradient. Values of 0 are mapped to white.
"""
# Mint
# Dense
# Burg
colorscale = px.colors.sequential.Mint
# Create a custom continuous color scale for shades of blue
colorscale = [
"#ffffff",
"#f0f0f0",
"#e1e1e1",
"#d2d2d2",
"#c3c3c3",
"#b5b5b5",
"#a7a7a7",
"#999999",
"#8b8b8b",
"#7e7e7e",
"#707070",
"#636363",
"#575757",
"#4a4a4a",
"#3e3e3e",
"#333333",
"#272727",
"#1d1d1d",
"#121212",
"#000000"
]
max_click = max
min_click = min
colors_list = []
vals = []
for click in clicks:
if click < min:
colors_list.append("#ffffff")
continue
# Normalize the click value between min_click and max_click
normalized_click = (click - min_click) / (max_click - min_click)
# Map normalized click to a color in the colorscale
color_idx = int(normalized_click * (len(colorscale) - 1))
#print(normalized_click, color_idx)
vals.append(color_idx)
colors_list.append(colorscale[color_idx])
return colors_list, vals
letters = list(data.index)
# Function to generate the keyboard layout with offsets
def generate_keyboard_boxes3(colors, selected_letter=None):
x = np.arange(10) # X positions for the keys
y = np.array([2, 1, 0]) # Y positions for the rows
offsets = [0, 0.25, 0.75] # Offsets for the rows
boxes = []
color_idx = 0 # Color index
for i in range(len(y)): # For each row
for j in range(len(x)): # For each key in the row
x0 = x[j] + offsets[i] # Calculate x0 with offset
y0 = y[i] # y0 position
x1 = x0 + 1 # x1 position
y1 = y0 + 1 # y1 position
#letter = letters[0]
#letter = round(s1[color_idx], 2)
letter = None
boxes.append(create_box(x0, y0, x1, y1, colors[color_idx % len(colors)], letter)) # Create and append the box with optional letter
color_idx += 1 # Increment color index
return boxes
colorscale = [
"#ffffff",
"#f0f0f0",
"#e1e1e1",
"#d2d2d2",
"#c3c3c3",
"#b5b5b5",
"#a7a7a7",
"#999999",
"#8b8b8b",
"#7e7e7e",
"#707070",
"#636363",
"#575757",
"#4a4a4a",
"#3e3e3e",
"#333333",
"#272727",
"#1d1d1d",
"#121212",
"#000000"
]
scores = [1.000086,1.000705,1.200568,1.043105,1.131275,1.003043,1.011047,1.084953,1.108289,1.01305,1,1,1.063747,1.104655,1.005745,1.002861,1.162576,1.144321,1.06597,1.172852,1.000114,1,1.004235,1.00383,1.005447,1.071739,1.001518,1.000022,1.000899,1]
letter = qwerty_layout[0] + qwerty_layout[1] + qwerty_layout[2]
# Create the figure
default_colors, vals = clicks_to_colors3(scores, min(scores), max(scores))
boxes = generate_keyboard_boxes3(default_colors)
fig = go.Figure()
# Add the initial boxes to the figure
for box in boxes:
fig.add_trace(box)
# Define the manual position for the text
text_x = 0.5 # Middle of the box in the x-axis
text_y = 2.5 # Middle of the box in the y-axis
offsets = [.5, .75, 1.25]
idx = 0
for row in range(3):
for column in range(10):
x = offsets[row] + text_x * column * 2
y = text_y - row
# Add annotation for the text
fig.add_annotation(
x=x,
y=y,
text = letter[idx].upper(),
showarrow=False,
font=dict(size=40),
xanchor='center',
yanchor='middle'
)
idx += 1
# Update the layout to include the dropdown menu
fig.update_layout(
title=dict(
font=dict(size=40, color='black'), # Set the font size and color for the title
y=0.95, # Adjust the vertical position of the title
x=0.24, # Center the title horizontally
xanchor='center',
yanchor='top'
),
title_font_color='black', # Set the title font color to black
xaxis=dict(showgrid=False, zeroline=False, visible=False),
yaxis=dict(showgrid=False, zeroline=False, visible=False),
width=1000,
height=500,
plot_bgcolor='white',
)
# Update layout to ensure x-axis labels are at the bottom
fig.update_layout(
margin=dict(l=10, r=10, t=10, b=0) # Adjust margins to remove whitespace
)
fig
# Create a dummy heatmap for the colorscale legend
fig.add_trace(go.Heatmap(
z=[[0, 100]], # Dummy data for the heatmap
colorscale=colorscale, # Choose a colorscale
showscale=True, # Show the scale
colorbar=dict(
orientation='v',
x=1.05,
y=0.93,
thickness=15,
len=0.8,
xanchor='center',
yanchor='top',
tickvals=[0, 25, 50, 75, 100], # Custom tick values
ticktext=['0%', '5%', '10%', '15%', '20%']
),
opacity = 0,
hoverinfo='none'
))
fig
sc = pd.DataFrame(scores, index = letters)
sc.describe()
| 0 | |
|---|---|
| count | 30.000000 |
| mean | 1.046888 |
| std | 0.062799 |
| min | 1.000000 |
| 25% | 1.000754 |
| 50% | 1.005596 |
| 75% | 1.081650 |
| max | 1.200568 |